package cn.iocoder.yudao.module.resource.api;

import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.infra.service.file.FileService;
import cn.iocoder.yudao.module.infra.service.file.UploadFileResult;
import cn.iocoder.yudao.module.resource.api.model.AudioDTO;
import cn.iocoder.yudao.module.resource.controller.admin.audio.vo.AudioRelationSaveVo;
import cn.iocoder.yudao.module.resource.dal.dataobject.audio.LmAudioDO;
import cn.iocoder.yudao.module.resource.dal.dataobject.audiomark.AudioMarkDO;
import cn.iocoder.yudao.module.resource.dal.dataobject.cosplay.LmCosplayDO;
import cn.iocoder.yudao.module.resource.dal.dataobject.keyword.KeyWordDO;
import cn.iocoder.yudao.module.resource.dal.dataobject.tag.TagDO;
import cn.iocoder.yudao.module.resource.dal.mysql.audio.LmAudioMapper;
import cn.iocoder.yudao.module.resource.dal.mysql.audiomark.AudioMarkMapper;
import cn.iocoder.yudao.module.resource.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.resource.service.audio.LmAudioService;
import cn.iocoder.yudao.module.resource.service.audiomark.AudioMarkService;
import cn.iocoder.yudao.module.resource.service.cosplay.LmCosplayService;
import cn.iocoder.yudao.module.resource.service.keyword.KeyWordService;
import cn.iocoder.yudao.module.resource.service.tag.TagService;
import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

/**
 * 音频api实现
 *
 * @author: wds_prc
 * @since: 2024/7/28 15:24
 */
@Service
@Slf4j
public class AudioServiceApiImpl implements IAudioServiceApi {

    @Resource
    private LmAudioMapper lmAudioMapper;
    @Resource
    private LmCosplayService lmCosplayService;
    @Resource
    private AudioMarkMapper audioMarkMapper;
    @Resource
    private LmAudioService lmAudioService;
    @Resource
    private AudioMarkService audioMarkService;
    @Resource
    private TagService tagService;
    @Resource
    private KeyWordService keyWordService;
    @Resource
    private FileService fileService;

    @Override
    public List<AudioDTO> queryAudioByTag(Integer roleId, List<String> tagList) {
        return queryAudio(roleId, tagList, new ArrayList<>(1));
    }

    @Override
    public List<AudioDTO> queryAudioByKeyword(Integer roleId, List<String> keyWordList) {
        return queryAudio(roleId, new ArrayList<>(1), keyWordList);
    }

    private List<AudioDTO> queryAudio(Integer roleId, List<String> tagList, List<String> keyWordList) {

        if (tagList == null) {
            tagList = new ArrayList<>();
        }
        if (keyWordList == null) {
            keyWordList = new ArrayList<>();
        }
        if (tagList.isEmpty() && keyWordList.isEmpty()) {
            return new ArrayList<>(1);
        }
        if (tagList.isEmpty()) {
            tagList.add("-");
        }
        if (keyWordList.isEmpty()) {
            keyWordList.add("-");
        }


        // 先查询角色绑定的音频
        LmCosplayDO cosplay = lmCosplayService.getCosplay(roleId);
        // 声纹id，角色绑定的声纹id
        Integer audioToneId = null;
        if (cosplay != null) {
            audioToneId = cosplay.getAudioToneId();
        }
        if (audioToneId == null) {
            throw new ServiceException(ErrorCodeConstants.AUDIO_TONE_NOT_EXISTS);
        }

        List<LmAudioDO> lmAudioDOS = audioMarkMapper.queryAudioByMark(audioToneId, tagList, keyWordList);
        List<Long> fileIdList = lmAudioDOS.stream().map(LmAudioDO::getFileId).collect(Collectors.toList());
        final Map<Long, Integer> resourceDurationSec = CollectionUtils.convertMap(lmAudioDOS, v -> v.getFileId(), v -> v.getAudioTime());
        List<AudioDTO> audioDtos = this.toAudioDtoList(lmAudioDOS);
        List<UploadFileResult> fileResults = fileService.getFile(fileIdList, resourceDurationSec);
        Map<Long, UploadFileResult> fileResultMap = fileResults.stream()
                .collect(Collectors.toMap(UploadFileResult::getFileId, c -> c));
        for (AudioDTO audioDto : audioDtos) {
            UploadFileResult uploadFileResult = fileResultMap.get(audioDto.getFileId());
            if (uploadFileResult != null) {
                audioDto.setFilePath(uploadFileResult.getPath());
                audioDto.setFileUrl(uploadFileResult.getFileUrl());
            }
        }
        return audioDtos;
    }

    @Override
    public List<AudioDTO> queryAudioByAllTag(Integer roleId, List<String> tagList) {
        // 所有tagName的集合
        List<TagDO> tagDOList = tagService.getTagListByNames(tagList);
        if (tagDOList.isEmpty()) {
            log.warn("未查询到tag信息，tag: {}", tagList);
            return Collections.EMPTY_LIST;
        }
        Set<Integer> tagIds = tagDOList.stream().map(c -> c.getId()).collect(Collectors.toSet());
//        if (tagIds.size() != tagList.size()) {
//            log.warn("有不存在的标签，返回空. tagList: {}", tagList);
//            return Collections.EMPTY_LIST;
//        }
        // 先根据tag查询出来所有tag对应的音频
        List<AudioDTO> audioDtoList = this.queryAudioByTag(roleId, tagList);
        if (audioDtoList.isEmpty()) {
            log.warn("未查询到tag标记的音频,tag: {}", tagList);
            return Collections.EMPTY_LIST;
        }
        List<Integer> audioIdList = audioDtoList.stream().map(c -> c.getAudioId()).collect(Collectors.toList());
        // 查询被标识的
        List<AudioMarkDO> audioMarkDOList = audioMarkService.getByAudioMarkIdList(audioIdList);
        if (audioMarkDOList.isEmpty()) {
            log.warn("音频可能都被删除了，未查询到，audioIdList: {}", audioIdList);
            return Collections.EMPTY_LIST;
        }
        Map<Integer, Set<Integer>> audioGroup = new HashMap<>();
        for (AudioMarkDO audioMarkDO : audioMarkDOList) {
            Set<Integer> tagSet = audioGroup.getOrDefault(audioMarkDO.getAudioId(), new TreeSet<>());
            if (audioMarkDO.getTagId() != null) {
                tagSet.add(audioMarkDO.getTagId());
            }
            if (!audioGroup.containsKey(audioMarkDO.getAudioId())) {
                audioGroup.put(audioMarkDO.getAudioId(), tagSet);
            }
        }
        // 查找匹配所有tag的音频
        List<AudioDTO> matchAudioList = audioDtoList.stream().filter(c -> {
            Set<Integer> tagSet = audioGroup.get(c.getAudioId());
            if (tagSet == null) {
                return false;
            }
            return tagSet.containsAll(tagIds);
        }).collect(Collectors.toList());

        return matchAudioList;
    }

    @Override
    public List<AudioDTO> queryAudioByAllKeyword(Integer roleId, List<String> keywordList) {
        // 所有关键字的集合
        List<KeyWordDO> keyWordDOList = keyWordService.getKeywordListByNames(keywordList);
        if (keyWordDOList.isEmpty()) {
            log.warn("未查询到keyword信息，tag: {}", keywordList);
            return Collections.EMPTY_LIST;
        }
        Set<Integer> keywordIds = keyWordDOList.stream().map(c -> c.getId()).collect(Collectors.toSet());
        if (keywordIds.size() != keywordList.size()) {
            log.warn("参数中有不存在关键字,返回空,tagList: {}", keywordList);
            return Collections.EMPTY_LIST;
        }
        // 先根据关键字查询出来所有关键字对应的音频
        List<AudioDTO> audioDtoList = this.queryAudioByKeyword(roleId, keywordList);
        if (audioDtoList.isEmpty()) {
            log.warn("未查询到tag标记的音频,tag: {}", keywordList);
            return Collections.EMPTY_LIST;
        }
        List<Integer> audioIdList = audioDtoList.stream().map(c -> c.getAudioId()).collect(Collectors.toList());
        // 查询被标识的
        List<AudioMarkDO> audioMarkDOList = audioMarkService.getByAudioMarkIdList(audioIdList);
        if (audioMarkDOList.isEmpty()) {
            log.warn("音频可能都被删除了，未查询到，audioIdList: {}", audioIdList);
            return Collections.EMPTY_LIST;
        }
        Map<Integer, Set<Integer>> audioGroup = new HashMap<>();
        for (AudioMarkDO audioMarkDO : audioMarkDOList) {
            Set<Integer> keywordSet = audioGroup.getOrDefault(audioMarkDO.getAudioId(), new TreeSet<>());
            if (audioMarkDO.getKeyWordId() != null) {
                keywordSet.add(audioMarkDO.getKeyWordId());
            }
            if (!audioGroup.containsKey(audioMarkDO.getAudioId())) {
                audioGroup.put(audioMarkDO.getAudioId(), keywordSet);
            }
        }
        // 查找匹配所有keyword的音频
        List<AudioDTO> matchAudioList = audioDtoList.stream().filter(c -> {
            Set<Integer> keywordSet = audioGroup.get(c.getAudioId());
            if (keywordSet == null) {
                return false;
            }
            return keywordSet.containsAll(keywordIds);
        }).collect(Collectors.toList());

        return matchAudioList;
    }

    private List<AudioDTO> toAudioDtoList(List<LmAudioDO> lmAudioDOS) {
        List<Integer> audioList = lmAudioDOS.stream().map(c -> c.getId()).collect(Collectors.toList());
        List<AudioMarkDO> audioMarkDOList = audioMarkService.getByAudioMarkIdList(audioList);

        List<AudioDTO> audioDtos = lmAudioDOS.stream().map(c -> {
            AudioDTO audioDto = this.toAudioDto(c);
            audioDto.setRelationType(c.getRelationType());
            this.setRelationContent(audioDto, c.getRelationContent());
            // 找到当前音频绑定的标签及关键字
            List<AudioMarkDO> audioMarkOfAudioList = audioMarkDOList.stream().filter(v -> v.getAudioId().equals(c.getId())).collect(Collectors.toList());
            List<Integer> tagIdList = audioMarkOfAudioList.stream().filter(f -> f.getTagId() != null).map(f -> f.getTagId()).collect(Collectors.toList());
            List<String> tagList = tagService.getTagListByIds(tagIdList);
            audioDto.setTagNameList(tagList);
            List<Integer> keywordIdList = audioMarkOfAudioList.stream().filter(f -> f.getKeyWordId() != null).map(f -> f.getKeyWordId()).collect(Collectors.toList());
            List<String> keywordList = keyWordService.getKeyWordListByIds(keywordIdList);
            audioDto.setKeywordNameList(keywordList);
            return audioDto;
        }).collect(Collectors.toList());
        return audioDtos;
    }

    private AudioDTO toAudioDto(LmAudioDO c) {
        AudioDTO audioDto = new AudioDTO();
        audioDto.setAudioId(c.getId());
        audioDto.setAudioName(c.getName());
        audioDto.setAudioText(c.getTextDesc());
        audioDto.setFileId(c.getFileId());
        return audioDto;
    }

    @Override
    public List<AudioDTO> queryAudioByIds(Collection<Integer> ids) {
        List<LmAudioDO> audioDOList = this.lmAudioService.getAudioByIds(ids);
        List<AudioDTO> audioDtos = this.toAudioDtoList(audioDOList);
        return audioDtos;
    }

    private void setRelationContent(AudioDTO audioDto, String relationContent) {
        AudioDTO.ReplyObjectDTO replyObjectDTO = new AudioDTO.ReplyObjectDTO();
        List<AudioDTO.ReplyItemDTO> replyItemList = new ArrayList<>();
        replyObjectDTO.setReplyItemList(replyItemList);
        audioDto.setRelationObject(replyObjectDTO);
        if (relationContent != null) {
            List<AudioRelationSaveVo> audioRelationSaveVos = JSON.parseArray(relationContent, AudioRelationSaveVo.class);
            for (AudioRelationSaveVo audioRelationSaveVo : audioRelationSaveVos) {
                AudioDTO.ReplyItemDTO replyItemDTO = new AudioDTO.ReplyItemDTO();
                replyItemDTO.setDefaultCondition(audioRelationSaveVo.isDefaultCon());
                LmAudioDO audio = lmAudioService.getAudio(audioRelationSaveVo.getAudioId());
                if (audio != null) {
                    AudioDTO subAudioDto = this.toAudioDto(audio);
                    replyItemDTO.setReplyAudio(subAudioDto);
                }
                if (audioRelationSaveVo.getIds() != null || audioRelationSaveVo.getIds().size() > 0) {
                    List<KeyWordDO> keyWordList = keyWordService.getKeyWordList(audioRelationSaveVo.getIds());
                    List<String> keywordNames = keyWordList.stream().map(c -> c.getKeyWord()).collect(Collectors.toList());
                    replyItemDTO.setKeywordNameList(keywordNames);
                }
                replyItemList.add(replyItemDTO);
            }
        }
    }
}
